package org.codefinger.dao.util.magic;

import java.util.HashMap;
import java.util.Map;

import org.codefinger.dao.asm.BaseTypeInfo;
import org.codefinger.dao.asm.Opcodes;
import org.codefinger.dao.util.Lang;

public class AsmUtil {

	private AsmUtil() {
	}

	public static final int							CODE_VERSION		= Opcodes.V1_5;

	private static final Map<String, BaseTypeInfo>	BASE_TYPE_INFO_MAP	= new HashMap<String, BaseTypeInfo>();

	static {
		BASE_TYPE_INFO_MAP.put("byte", new BaseTypeInfo(getAsmClassName(Byte.class), "B", getAsmTypeName(Byte.class)));
		BASE_TYPE_INFO_MAP.put("short", new BaseTypeInfo(getAsmClassName(Short.class), "S", getAsmTypeName(Short.class)));
		BASE_TYPE_INFO_MAP.put("int", new BaseTypeInfo(getAsmClassName(Integer.class), "I", getAsmTypeName(Integer.class)));
		BASE_TYPE_INFO_MAP.put("long", new BaseTypeInfo(getAsmClassName(Long.class), "J", getAsmTypeName(Long.class)));
		BASE_TYPE_INFO_MAP.put("float", new BaseTypeInfo(getAsmClassName(Float.class), "F", getAsmTypeName(Float.class)));
		BASE_TYPE_INFO_MAP.put("double", new BaseTypeInfo(getAsmClassName(Double.class), "D", getAsmTypeName(Double.class)));
		BASE_TYPE_INFO_MAP.put("char", new BaseTypeInfo(getAsmClassName(Character.class), "C", getAsmTypeName(Character.class)));
		BASE_TYPE_INFO_MAP.put("boolean", new BaseTypeInfo(getAsmClassName(Boolean.class), "Z", getAsmTypeName(Boolean.class)));
	}

	public static String getAsmClassName(Class<?> clazz) {
		String className = clazz.getName();
		BaseTypeInfo baseTypeInfo = BASE_TYPE_INFO_MAP.get(className);
		if (baseTypeInfo != null) {
			return baseTypeInfo.getAsmPackageClassName();
		}
		return className.replaceAll("\\.", "/");
	}

	public static String getAsmTypeName(Class<?> clazz) {
		String className = clazz.getName();
		BaseTypeInfo baseTypeInfo = BASE_TYPE_INFO_MAP.get(className);
		if (baseTypeInfo != null) {
			return baseTypeInfo.getAsmBaseClassName();
		}
		if (!className.startsWith("[")) {
			className = Lang.joinString("L", className, ";");
		}
		return className.replaceAll("\\.", "/");
	}

	public static String getMethodDesc(Class<?> returnType, Class<?>... parameters) {
		StringBuilder desc = new StringBuilder("(");
		for (Class<?> parameter : parameters) {
			desc.append(getAsmTypeName(parameter));
		}
		return desc.append(")").append(returnType == null || Void.class.isAssignableFrom(returnType) ? "V" : getAsmTypeName(returnType)).toString();
	}

	public static String getConstructorDesc(Class<?>... parameters) {
		StringBuilder desc = new StringBuilder("(");
		for (Class<?> parameter : parameters) {
			desc.append(getAsmTypeName(parameter));
		}
		return desc.append(")V").toString();
	}

	public static BaseTypeInfo getBaseTypeInfo(String className) {
		return BASE_TYPE_INFO_MAP.get(className);
	}

}